using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using Object = UnityEngine.Object;

/// <summary>
/// ab包管理器
/// </summary>
public class ABManager : SingletonBase<ABManager>
{
    // 防止ab包重复加载
    private Dictionary<String, AssetBundle> abDic = new Dictionary<string, AssetBundle>();

    // 主包
    private AssetBundle _mainAB;

    // 依赖包获取用的配置文件
    private AssetBundleManifest _manifest;

    /// <summary>
    /// ab包存放路径，只能get(读)
    /// </summary>
    private String PathUrl => Application.streamingAssetsPath + "/";

    private string MainABName
    {
        get
        {
#if UNITY_IOS
        return "IOS";
#elif UNITY_ANDROID
            return "Android";
#else
            return "PC";
#endif
        }
    }


    private void LoadAB(string abName)
    {
        // 加载主包
        if (_mainAB == null)
        {
            _mainAB = AssetBundle.LoadFromFile(PathUrl + MainABName);
            _manifest = _mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        }

        // 获取依赖关系
        AssetBundle ab;
        var strs = _manifest.GetAllDependencies(abName);
        // 加载当前AB包的依赖包
        for (int i = 0; i < strs.Length; i++)
        {
            if (!abDic.ContainsKey(strs[i]))
            {
                ab = AssetBundle.LoadFromFile(PathUrl + strs[i]);
                abDic.Add(strs[i], ab);
            }
        }

        // 加载来源包
        if (!abDic.ContainsKey(abName))
        {
            ab = AssetBundle.LoadFromFile(PathUrl + abName);
            abDic.Add(abName, ab);
        }
    }

    /// <summary>
    /// 同步加载 - 不指定类型
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <returns></returns>
    public Object LoadRes(string abName, string resName)
    {
        LoadAB(abName);
        // 加载资源
        Object obj = abDic[abName].LoadAsset(resName);
        // 如果资源是GameObject直接实例化了，再返回给外部
        if (obj is GameObject)
        {
            return Instantiate(obj);
        }

        return obj;
    }

    /// <summary>
    /// 同步加载 - 指定类型
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <returns></returns>
    public Object LoadRes(string abName, string resName, Type type)
    {
        LoadAB(abName);
        // 加载资源
        Object obj = abDic[abName].LoadAsset(resName, type);
        // 如果资源是GameObject直接实例化了，再返回给外部
        if (obj is GameObject)
        {
            return Instantiate(obj);
        }

        return obj;
    }

    /// <summary>
    /// 同步加载 - 泛型
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <returns></returns>
    public T LoadRes<T>(string abName, string resName) where T : Object
    {
        LoadAB(abName);
        // 加载资源
        T obj = abDic[abName].LoadAsset<T>(resName);
        // 如果资源是GameObject直接实例化了，再返回给外部
        if (obj is GameObject)
        {
            return Instantiate(obj);
        }

        return obj;
    }


    /// <summary>
    /// 异步加载 - 不指定类型
    /// 这里异步只是加载资源异步，加载AB包没有异步
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="callback"></param>
    /// <returns></returns>
    public void LoadResAsync(string abName, string resName, UnityAction<Object> callback)
    {
        StartCoroutine(RealLoadResAsync(abName, resName, callback));
    }

    private IEnumerator RealLoadResAsync(string abName, string resName, UnityAction<Object> callback)
    {
        LoadAB(abName);
        // 加载资源
        AssetBundleRequest request = abDic[abName].LoadAssetAsync(resName);
        yield return request;
        // 如果资源是GameObject直接实例化了，再返回给外部
        // if (request.asset is GameObject)
        // {
        //     callback(Instantiate(request.asset));
        // }

        callback(request.asset);
    }

    /// <summary>
    /// 异步加载 - 指定类型
    /// 这里异步只是加载资源异步，加载AB包没有异步
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="type"></param>
    /// <param name="callback"></param>
    /// <returns></returns>
    public void LoadResAsync(string abName, string resName, Type type, UnityAction<Object> callback)
    {
        StartCoroutine(RealLoadResAsync(abName, resName, type, callback));
    }

    private IEnumerator RealLoadResAsync(string abName, string resName, Type type, UnityAction<Object> callback)
    {
        LoadAB(abName);
        // 加载资源
        AssetBundleRequest request = abDic[abName].LoadAssetAsync(resName, type);
        yield return request;
        // 如果资源是GameObject直接实例化了，再返回给外部
        // if (request.asset is GameObject)
        // {
        //     callback(Instantiate(request.asset));
        // }

        callback(request.asset);
    }

    /// <summary>
    /// 异步加载 - 泛型
    /// 这里异步只是加载资源异步，加载AB包没有异步
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="callback"></param>
    /// <returns></returns>
    public void LoadResAsync<T>(string abName, string resName, UnityAction<T> callback) where T : Object
    {
        StartCoroutine(RealLoadResAsync(abName, resName, callback));
    }

    private IEnumerator RealLoadResAsync<T>(string abName, string resName, UnityAction<T> callback) where T : Object
    {
        LoadAB(abName);
        // 加载资源
        AssetBundleRequest request = abDic[abName].LoadAssetAsync<T>(resName);
        yield return request;
        // 如果资源是GameObject直接实例化了，再返回给外部
        // if (request.asset is GameObject)
        // {
        //     callback(Instantiate(request.asset) as T);
        // }

        callback(request.asset as T);
    }

    /// <summary>
    /// 单个包卸载
    /// </summary>
    /// <param name="abName"></param>
    public void UnLoad(string abName)
    {
        if (abDic.ContainsKey(abName))
        {
            abDic[abName].Unload(false);
            abDic.Remove(abName);
        }
    }

    /// <summary>
    /// 所有包卸载
    /// </summary>
    public void ClearAB()
    {
        AssetBundle.UnloadAllAssetBundles(false);
        abDic.Clear();
        _mainAB = null;
        _manifest = null;
    }
}